//
//  UploadDatabase.swift
//  SitPosition
//
//  Created by apple on 2019/2/10.
//  Copyright © 2019 apple. All rights reserved.
//

import Foundation
import SQLite

/**
 * 上传任务
 */
struct UploadTask : Equatable {
    
    // 上传任务状态
    enum State : Int, CaseIterable {
        case initial=0
        case uploading=1
        case paused=2
        case finished=3
        case failed=0xf
    }
    
    // 表名
    static let dbTable: String = "uploadTask"
    
    // MARK: - Properties
    
    let key: String             // 键值：查找本地文件、构造七牛URL的关键
    let userId: String          // 用户ID
    let extra: String?          // 附加信息：通常为JSON串
    var progress: Float = 0     // 上传进度
    var url: String?            // 七牛URL
    var state: State = .initial // 上传任务状态
    
    static let table = Table(UploadTask.dbTable)
    static let keyField = Expression<String>("key")
    static let userIdField = Expression<String>("userId")
    static let extraField = Expression<String?>("extra")
    static let progressField = Expression<String?>("progress")
    static let urlField = Expression<String?>("url")
    static let stateField = Expression<Int>("state")
    
    // MARK: - Object lifecycle
    
    // CREATE TABLE IF NOT EXIST
    static func createIfNeeded(_ db: SQLite.Connection) throws -> Void {
        try db.run(UploadTask.table.create(ifNotExists: true) { (t) in
            t.column(UploadTask.keyField, primaryKey: true)
            t.column(UploadTask.userIdField)
            t.column(UploadTask.extraField)
            t.column(UploadTask.progressField)
            t.column(UploadTask.urlField)
            t.column(UploadTask.stateField)
        })
    }
    
    init(key: String, userId: String, extra: String?) {
        self.key = key
        self.userId = userId
        self.extra = extra
    }
    
    init(key: String, userId: String,
         extra: String?, progress: Float,
         url: String?, state: State) {
        self.key = key
        self.userId = userId
        self.extra = extra
        self.progress = progress
        self.url = url
        self.state = state
    }
    
    // MARK: - Equatable
    
    public static func == (lhs: UploadTask, rhs: UploadTask) -> Bool {
        return lhs.key == rhs.key
    }
}

// MARK: - Private Methods

extension UploadTask {
    
    // 获取本地缓存文件
    public func localCacheFilePath() throws -> String {
        let cacheDirPath: String = try Paths.uploadCachePath()
        return (cacheDirPath as NSString).appendingPathComponent(key)
    }
}


// MARK: - SQLite Database

extension UploadTask {
    
    // 获取所有指定状态的任务
    // @states - 要查询的状态集合
    // @userId - 任务所属用户的ID
    static func all(by states: [UploadTask.State], userId: String?) -> [UploadTask]? {
        guard let db: SQLite.Connection = Database.shared.db else {
            return nil
        }
        do {
            let items: AnySequence<Row>
            var query: SQLite.Table
            if states.isEmpty {
                query = UploadTask.table
            } else {
                let values = states.map { (state) -> Int in
                    state.rawValue
                }
                query = UploadTask.table.filter(values.contains(UploadTask.stateField))
            }
            
            if let userId_ = userId {
                query = query.filter(UploadTask.userIdField == userId_)
            }
            items = try db.prepare(query)
            
            return items.map { (row) -> UploadTask in
                let progressStr: String = row[UploadTask.progressField] ?? "0"
                let progress: Float = (progressStr as NSString).floatValue
                return UploadTask(key: row[UploadTask.keyField],
                                  userId: row[UploadTask.userIdField],
                                  extra: row[UploadTask.extraField],
                                  progress: progress,
                                  url: row[UploadTask.urlField],
                                  state: State(rawValue: row[UploadTask.stateField]) ?? .initial)
            }
        } catch {
            dbgPrint("filter upload task occured error: \(error)")
            return nil
        }
    }
    
    // 获取指定keys对应的任务
    // @keys - 任务key列表
    static func all(by keys: [String]) -> [UploadTask]? {
        guard let db: SQLite.Connection = Database.shared.db else {
            return nil
        }
        do {
            let query: SQLite.Table = UploadTask.table.filter(keys.contains(UploadTask.keyField))
            let items: AnySequence<Row> = try db.prepare(query)
            
            return items.map { (row) -> UploadTask in
                let progressStr: String = row[UploadTask.progressField] ?? "0"
                let progress: Float = (progressStr as NSString).floatValue
                return UploadTask(key: row[UploadTask.keyField],
                                  userId: row[UploadTask.userIdField],
                                  extra: row[UploadTask.extraField],
                                  progress: progress,
                                  url: row[UploadTask.urlField],
                                  state: State(rawValue: row[UploadTask.stateField]) ?? .initial)
            }
        } catch {
            dbgPrint("filter upload task occured error: \(error)")
            return nil
        }
    }
    
    // 插入记录
    // @return - rowId 插入的记录ID
    @discardableResult
    public static func insert(with task: UploadTask) -> Int64? {
        guard let db: SQLite.Connection = Database.shared.db else {
            return nil
        }
        do {
            let insert = UploadTask.table.insert(
                UploadTask.keyField <- task.key,
                UploadTask.userIdField <- task.userId,
                UploadTask.extraField <- task.extra,
                UploadTask.progressField <- "\(task.progress)",
                UploadTask.stateField <- task.state.rawValue
            )
            return try db.run(insert)
        } catch {
            return nil
        }
    }
    
    // 删除记录
    // @primaryKey - 记录主键：rowid或key
    @discardableResult
    public static func delete(by primaryKey: Database.PrimaryKey) -> Bool {
        guard let db: SQLite.Connection = Database.shared.db else {
            return false
        }
        do {
            let query: SQLite.Table
            switch primaryKey {
            case let .rowid(rowid):
                query = UploadTask.table.filter(Database.rowId == rowid)
            case let .custom(key):
                query = UploadTask.table.filter(UploadTask.keyField == key)
            }
            try db.run(query.delete())
            return true
        } catch {
            return false
        }
    }
    
    // 批量删除记录
    // @rowid - 记录ID
    @discardableResult
    public static func delete(by keys: [String]) -> Bool {
        guard let db: SQLite.Connection = Database.shared.db else {
            return false
        }
        do {
            let query: SQLite.Table
            query = UploadTask.table.filter(keys.contains(UploadTask.keyField))
            try db.run(query.delete())
            return true
        } catch {
            return false
        }
    }
    
    // 更新进度
    @discardableResult
    public static func updateProgress(by primaryKey: Database.PrimaryKey, progress: Float) -> Bool {
        guard let db: SQLite.Connection = Database.shared.db else {
            return false
        }
        do {
            let query: SQLite.Table
            switch primaryKey {
            case let .rowid(rowid):
                query = UploadTask.table.filter(Database.rowId == rowid)
            case let .custom(key):
                query = UploadTask.table.filter(UploadTask.keyField == key)
            }
            try db.run(query.update(UploadTask.progressField <- "\(progress)"))
            return true
        } catch {
            return false
        }
    }
    
    // 更新状态
    @discardableResult
    public static func updateState(by primaryKey: Database.PrimaryKey, state: State) -> Bool {
        guard let db: SQLite.Connection = Database.shared.db else {
            return false
        }
        do {
            let query: SQLite.Table
            switch primaryKey {
            case let .rowid(rowid):
                query = UploadTask.table.filter(Database.rowId == rowid)
            case let .custom(key):
                query = UploadTask.table.filter(UploadTask.keyField == key)
            }
            try db.run(query.update(UploadTask.stateField <- state.rawValue))
            return true
        } catch {
            return false
        }
    }
    
    @discardableResult
    public static func updateURL(by primaryKey: Database.PrimaryKey, url: String?) -> Bool {
        guard let db: SQLite.Connection = Database.shared.db else {
            return false
        }
        do {
            let query: SQLite.Table
            switch primaryKey {
            case let .rowid(rowid):
                query = UploadTask.table.filter(Database.rowId == rowid)
            case let .custom(key):
                query = UploadTask.table.filter(UploadTask.keyField == key)
            }
            try db.run(query.update(UploadTask.urlField <- url))
            return true
        } catch {
            return false
        }
    }
}
